/*******************************************************************************
 Jimm - Mobile Messaging - J2ME ICQ clone
 Android version
 Copyright (C) 2003-11  Jimm Project

 This program is free software; you can redistribute it and/or
 modify it under the terms of the GNU General Public License
 as published by the Free Software Foundation; either version 2
 of the License, or (at your option) any later version.

 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.

 You should have received a copy of the GNU General Public License
 along with this program; if not, write to the Free Software
 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 ********************************************************************************
 File: src/jimm/app/InitializeActivity.java
 Version: 3  Date: 23.07.2011
 Author(s): Ashikhmin Kirill
 *******************************************************************************/

package org.jimm.service;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.List;

import org.jimm.app.ContactListActivity;
import org.jimm.app.NotificationMan;
import org.jimm.app.R;
import org.jimm.db.JimmDatabase.Accounts;
import org.jimm.db.JimmDatabase.Contacts;
import org.jimm.db.JimmDatabase.Groups;
import org.jimm.db.JimmDatabase.History;
import org.jimm.engine.AccountsManager;
import org.jimm.im.Account;
import org.jimm.im.ConnectionListener;
import org.jimm.im.Contact;
import org.jimm.im.ContactListListener;
import org.jimm.im.MessagesListener;

import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
import android.os.PowerManager;
import android.text.TextUtils;
import android.util.Log;
import android.widget.Toast;

public final class JimmService extends Service implements ConnectionListener,
MessagesListener, ContactListListener {
    public static final String ACTION_FOREGROUND = "org.jimm.app.FOREGROUND";
    static final String ACTION_BACKGROUND = "org.jimm.app.BACKGROUND";

	private static final String LOG_TAG = "Jimm:JimmService";

	private static AccountsManager accountsManager;
	private final LocalBinder localBinder = new LocalBinder();
	public MyHandler handler;
	private Preferences preferences;
	private Notifications notifications;
	private ContentResolver contentResolver;
	private PowerManager.WakeLock wakeLock;
    
    
    private static final Class[] mStartForegroundSignature = new Class[] {
        int.class, Notification.class};
    private static final Class[] mStopForegroundSignature = new Class[] {
        boolean.class};
    
    private NotificationManager mNM;
    private Method mStartForeground;
    private Method mStopForeground;
    private Object[] mStartForegroundArgs = new Object[2];
    private Object[] mStopForegroundArgs = new Object[1];
    
    public class LocalBinder extends Binder {
		public JimmService getService() {
			return JimmService.this;
		}
	}

	public class MyHandler extends Handler {

		public static final int MESSAGE_SHOW_TOAST = 0;

		@Override
		public void handleMessage(android.os.Message msg) {
			
			Log.i(LOG_TAG,"textmsg= "+msg);
			
			String text = (String) msg.obj;
			if (text.equals("exit")){
				stopSelf();
				
				//stopService(this);
				//getAccountsManager().disconnectAll();
				//unbindService(JimmApplication.JimmServiceConnection);
				//JimmService=null;
			}
			switch (msg.what) {
			case MESSAGE_SHOW_TOAST:
				Toast.makeText(ContactListActivity.context, text,
						Toast.LENGTH_SHORT).show();
				Log.i(LOG_TAG,"text= "+text);
				break;
			default:
				Log.d(LOG_TAG, "Unknown Message " + msg.what);
			}
		}
	}
    
    @Override
    public void onCreate() {
		super.onCreate();
        Log.i(LOG_TAG,"onStart!!!!!!!!!!!!!!!!!!!!!!!!!");
		PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
		wakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "Jimm");
		wakeLock.acquire();

		contentResolver = getContentResolver();

		handler = new MyHandler();
		preferences = new Preferences(this);
		notifications = new Notifications(this, preferences);
		accountsManager = new AccountsManager(getContentResolver());
		accountsManager.setConnectionListener(this);
		accountsManager.registerContactListListener(this);
		accountsManager.registerMessagesListener(this);
		
		Log.i(LOG_TAG,"getAccountsManager()"+getAccountsManager());
		
        mNM = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
        try {
            mStartForeground = getClass().getMethod("startForeground",
                    mStartForegroundSignature);
            mStopForeground = getClass().getMethod("stopForeground",
                    mStopForegroundSignature);
        } catch (NoSuchMethodException e) {
            // Running on an older platform.
            mStartForeground = mStopForeground = null;
        }
    }
    
    @Override
	public void onDestroy() {
		
		if (wakeLock != null) {
			wakeLock.release();
		}
		 Log.i(LOG_TAG,"onDestroy!!!!!!!!!!!!!!!!!!!!!!!!!");
		accountsManager.unregisterContactListListener(this);
		accountsManager.unregisterMessagesListener(this);
		Log.i(LOG_TAG, "Service Destroy");
		stopForegroundCompat(R.string.app_name);
		stopSelf();
		super.onDestroy();
	}
	

	@Override
	public IBinder onBind(Intent arg0) {
		return localBinder;
	}

	public final static AccountsManager getAccountsManager() {
		return accountsManager;
		
	}

	public final Preferences getPreferences() {
		return preferences;
	}

	public void stop() {
		Log.i(LOG_TAG, "disconnect");
		accountsManager.disconnectAll();
	}

	public void connect() {
		Log.i(LOG_TAG, "connect");
		accountsManager.connectAll();
	}

	/* ConnectionListener */

	@Override
	public void onLogin() {
		handler.sendMessage(handler.obtainMessage(MyHandler.MESSAGE_SHOW_TOAST,
				"Connected" ));
		notifications.onLogin();
	}

	@Override
	public void onLogout() {
		handler.sendMessage(handler.obtainMessage(MyHandler.MESSAGE_SHOW_TOAST,
				"Disconnected"));
	}

	@Override
	public void onConnectionError(String error) {
		handler.sendMessage(handler.obtainMessage(MyHandler.MESSAGE_SHOW_TOAST,
				"Connection Error" + " (" + error + ")"));
	}

	/* MessagesListener */

	@Override
	public void onMessage(String accountID, String message, Contact contact) {
		// О-о!
		notifications.onInputMessage();
		String MessNick = null;
		String AccLogin = null;
		int AccType = 0;
		String AccTypeText = null;

		// FIXME сделать, чтобы передавался сразу ид аккаунта в бд
		Cursor c = contentResolver.query(Accounts.CONTENT_URI,
				new String[] { Accounts._ID , Accounts.LOGIN , Accounts.TYPE}, Accounts.LOGIN + "= ?",
				new String[] { accountID }, null);
		if (c.moveToFirst()) {
			// FIXME не надо тут тоже искать
			Cursor contCursor = contentResolver.query(Contacts.CONTENT_URI,
					new String[] { Contacts._ID, Contacts.NICK }, 
					Contacts.ACCOUNT + " = ? AND " + Contacts.CONTACT_ID + " = ?",
					new String[] { c.getString(0), contact.getID()}, 
					null);
			
			
			if (contCursor.moveToFirst()) {
				ContentValues values = new ContentValues();
				values.put(History.CONTACT, contCursor.getLong(0));
				values.put(History.MESSAGE, message);
				values.put(History.FLAGS, History.FLAG_RECEIVED_MESSAGE);
				contentResolver.insert(History.CONTENT_URI, values);
			}
			
			MessNick = contCursor.getString(1);
			AccLogin = c.getString(1);
			AccType = c.getInt(2);
			contCursor.close();
			switch (AccType) {
			case 1: AccTypeText="ICQ"; break;
			case 2: AccTypeText="Jabber"; break;
			default: AccTypeText="n/a"; break;
			}
		}
		c.close();
		
		NotificationMan.addNotification(this, MessNick, contact.getID(), message,AccLogin,AccTypeText);
	}

	/* ContactListListener */

	@Override
	public void onStatusChanged(Account account, Contact user, int oldStatus) {
		if ((oldStatus == Contact.MAIN_STATUS_OFFLINE)
				&& (user.getMainStatus() != Contact.MAIN_STATUS_OFFLINE))
			notifications.onContactOnline();
	}
	
	@Override
	public void onAdditionalStatusChanged(Account account, Contact user) {
	}

	/**
	 * update/create group name 
	 * 
	 * @param groupName
	 *            - Name group
	 */
	private final Cursor getGroup(String groupName) {
		Cursor groupCursor = contentResolver.query(Groups.CONTENT_URI, null,
				Groups.NAME + " = ?", new String[] { groupName }, null);
		if (groupCursor.moveToFirst())
			return groupCursor;

		groupCursor.close();
		ContentValues values = new ContentValues();
		values.put(Groups.NAME, groupName);
		Uri uri = contentResolver.insert(Groups.CONTENT_URI, values);
		groupCursor = contentResolver.query(uri, null, null, null, null);
		return groupCursor;
	}

	/**
	 * update/create info about contact 
	 * 
	 * @param accountDBId
	 *            - position account in db
	 * @param contact
	 *            - contact
	 */
	private final void storeContactToDB(int accountDBPos, Contact contact) {
		Integer groupId = null;
		String groupName = contact.getGroupName();
		if (!TextUtils.isEmpty(groupName)) {
			Cursor groupCursor = getGroup(groupName);
			if (groupCursor.moveToFirst())
				groupId = groupCursor.getInt(groupCursor
						.getColumnIndex(Groups._ID));
			groupCursor.close();
		}

		ContentValues values = new ContentValues();
		values.put(Contacts.ACCOUNT, accountDBPos);
		values.put(Contacts.CONTACT_ID, contact.getID());
		values.put(Contacts.NICK, contact.getNick());
		values.put(Contacts.GROUP, groupId);

		int count = contentResolver.update(Contacts.CONTENT_URI, values,
				Contacts.ACCOUNT + " = " + accountDBPos + " AND "
						+ Contacts.CONTACT_ID + " = " + contact.getID(), null);
		if (count < 1)
			contentResolver.insert(Contacts.CONTENT_URI, values);
	}

	private final Cursor getAccountCursor(Account account) {
		// TODO сделать индекс по Accounts.LOGIN
		return contentResolver.query(Accounts.CONTENT_URI,
				new String[] { Accounts._ID }, Accounts.LOGIN + "= ?",
				new String[] { account.getID() }, null);
	}

	@Override
	public void onAddUser(Account account, Contact user) {
		// FIXME сделать, чтобы передавался сразу ид аккаунта в бд
		Cursor c = getAccountCursor(account);
		if (c.moveToFirst())
			storeContactToDB(c.getInt(0), user);
		c.close();
	}

	@Override
	public void onDeleteUser(Account account, Contact user) {
		// TODO
	}

	@Override
	public void onChangeUserInfo(Account account, Contact user) {
		/// FIXME сделать, чтобы передавался сразу ид аккаунта в бд
		Cursor c = getAccountCursor(account);
		if (c.moveToFirst()) {
			ContentValues values = new ContentValues();
			values.put(Contacts.NICK, user.getNick());
			getContentResolver().update(
					Contacts.CONTENT_URI,
					values,
					Contacts.ACCOUNT + " = ? AND " + Contacts.CONTACT_ID
							+ " = ?",
					new String[] { c.getString(0), user.getID() });
		}
		c.close();
	}

	@Override
	public void onUpdateContactList(Account account, List<Contact> contactList) {
		Cursor accountCursor = getAccountCursor(account);
		if (accountCursor.moveToFirst()) {
			int accountPos = accountCursor.getInt(0);
			for (Contact contact : contactList)
				storeContactToDB(accountPos, contact);
		}
		accountCursor.close();
	}

    // This is the old onStart method that will be called on the pre-2.0
    // platform.  On 2.0 or later we override onStartCommand() so this
    // method will not be called.
    @Override
    public void onStart(Intent intent, int startId) {
        handleCommand(intent);
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        handleCommand(intent);
        // We want this service to continue running until it is explicitly
        // stopped, so return sticky.
        return START_STICKY;
    }

    void handleCommand(Intent intent) {
        if (ACTION_FOREGROUND.equals(intent.getAction())) {
            

            // Set the icon, scrolling text and timestamp
            Notification notification = new Notification(R.drawable.icon, getText(R.string.app_name),
                    System.currentTimeMillis());

            // The PendingIntent to launch our activity if the user selects this notification
            PendingIntent contentIntent = PendingIntent.getActivity(this, 0,
                    new Intent(this, org.jimm.app.ContactListActivity.class), 0);

            // Set the info for the views that show in the notification panel.
            notification.setLatestEventInfo(this, getText(R.string.app_name),
            		getText(R.string.App_running), contentIntent);
            
            startForegroundCompat(R.string.app_name, notification);
            
        }
        /*
        else if (ACTION_BACKGROUND.equals(intent.getAction())) {
            stopForegroundCompat(R.string.app_name);
        }*/
    }
    
    void startForegroundCompat(int id, Notification notification) {
        if (mStartForeground != null) {
            mStartForegroundArgs[0] = Integer.valueOf(id);
            mStartForegroundArgs[1] = notification;
            try {
                mStartForeground.invoke(this, mStartForegroundArgs);
            } catch (InvocationTargetException e) {
                Log.w(LOG_TAG, "Unable to invoke startForeground", e);
            } catch (IllegalAccessException e) {
                Log.w(LOG_TAG, "Unable to invoke startForeground", e);
            }
            return;
        }
        
        // Fall back on the old API.
        setForeground(true);
        mNM.notify(id, notification);
    }
    
    void stopForegroundCompat(int id) {
        if (mStopForeground != null) {
            mStopForegroundArgs[0] = Boolean.TRUE;
            try {
                mStopForeground.invoke(this, mStopForegroundArgs);
            } catch (InvocationTargetException e) {
                Log.w(LOG_TAG, "Unable to invoke stopForeground", e);
            } catch (IllegalAccessException e) {
                Log.w(LOG_TAG, "Unable to invoke stopForeground", e);
            }
            return;
        }
        // Fall back on the old API.
        mNM.cancel(id);
        setForeground(false);
    }
    

}
